Processing Time Analysis¶

This notebook allows you to select and visualize processing time and read time data from marker location files.

Setup¶

Configure the base directory and define functions to find folders containing MarkerLocationsGA_CouchShift files.

In [28]:
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.io as pio
import os
from pathlib import Path

# Configure Plotly for offline use in exports
pio.renderers.default = "notebook"
In [29]:
# Base directory for data
base_dir = r"C:\Users\kankean.kandasamy\Repo\KIM-QA-Analysis\Lyrebird Data"

# Column names for MarkerLocationsGA files (even if headers are missing)
GA_COLUMNS = [
    'Frame No', 'Time (sec)', 'Gantry', 
    'Marker_0_AP', 'Marker_0_LR', 'Marker_0_SI',
    'Marker_1_AP', 'Marker_1_LR', 'Marker_1_SI',
    'Marker_2_AP', 'Marker_2_LR', 'Marker_2_SI',
    'Marker_0_X', 'Marker_0_Y', 'Marker_1_X', 'Marker_1_Y', 'Marker_2_X', 'Marker_2_Y',
    'Marker_0_Correlation', 'Marker_0_Segmentation',
    'Marker_1_Correlation', 'Marker_1_Segmentation',
    'Marker_2_Correlation', 'Marker_2_Segmentation',
    'Joint Template', 'Processing Time (ms)', 'Read Time (ms)',
    'HeaderTimeTag', 'Filename'
]

# Find all subdirectories
def find_folders(base_path):
    folders = []
    for root, dirs, filenames in os.walk(base_path):
        # Check if this directory contains MarkerLocationsGA_CouchShift files
        has_ga_files = any('MarkerLocationsGA_CouchShift' in f and f.endswith('.txt') for f in filenames)
        if has_ga_files:
            relative_path = os.path.relpath(root, base_path)
            folders.append((relative_path, root))
    return sorted(folders)

# Find all MarkerLocationsGA_CouchShift files in a folder
def find_ga_files_in_folder(folder_path):
    files = []
    for filename in os.listdir(folder_path):
        if 'MarkerLocationsGA_CouchShift' in filename and filename.endswith('.txt'):
            full_path = os.path.join(folder_path, filename)
            files.append((filename, full_path))
    return sorted(files)

available_folders = find_folders(base_dir)
print(f"Found {len(available_folders)} folders with MarkerLocationsGA_CouchShift files")
Found 17 folders with MarkerLocationsGA_CouchShift files

Plotting Functions¶

Define the data reading and plotting logic.

In [22]:
def plot_file(file_path, x_axis_col='Time (sec)', title=None):
    """Read a single data file and create interactive plots"""
    try:
        # Try reading with headers first
        try:
            df = pd.read_csv(file_path, skipinitialspace=True)
            df.columns = df.columns.str.strip()
            # Check if we have the expected columns
            if 'Processing Time (ms)' not in df.columns:
                raise ValueError("Missing expected columns")
        except:
            # Read without headers and assign column names
            df = pd.read_csv(file_path, header=None, names=GA_COLUMNS, skipinitialspace=True)
        
        # Extract relevant columns
        x_data = df[x_axis_col]
        processing_time = df['Processing Time (ms)']
        read_time = df['Read Time (ms)']
        
        # Create subplots with 2 rows
        fig = make_subplots(
            rows=2, cols=1,
            subplot_titles=('Processing Time', 'Read Time'),
            vertical_spacing=0.12,
            shared_xaxes=True
        )
        
        # Add Processing Time trace (top panel)
        fig.add_trace(
            go.Scatter(
                x=x_data,
                y=processing_time,
                mode='lines+markers',
                name='Processing Time',
                line=dict(color='#1f77b4', width=2),
                marker=dict(size=4),
                hovertemplate=f'{x_axis_col}: %{{x}}<br>Processing Time: %{{y:.2f}} ms<extra></extra>'
            ),
            row=1, col=1
        )
        
        # Add Read Time trace (bottom panel)
        fig.add_trace(
            go.Scatter(
                x=x_data,
                y=read_time,
                mode='lines+markers',
                name='Read Time',
                line=dict(color='#ff7f0e', width=2),
                marker=dict(size=4),
                hovertemplate=f'{x_axis_col}: %{{x}}<br>Read Time: %{{y:.2f}} ms<extra></extra>'
            ),
            row=2, col=1
        )
        
        # Update axes labels
        fig.update_xaxes(title_text=x_axis_col, row=2, col=1)
        fig.update_yaxes(title_text='Processing Time (ms)', row=1, col=1)
        fig.update_yaxes(title_text='Read Time (ms)', row=2, col=1)
        
        # Use custom title if provided, otherwise generate from path
        if title is None:
            filename = os.path.basename(file_path)
            folder_name = os.path.basename(os.path.dirname(file_path))
            title = f'{folder_name} - {filename}'
        
        fig.update_layout(
            height=800,
            showlegend=True,
            hovermode='x unified',
            title=dict(
                text=f'<b>{title}</b>',
                x=0.5,
                xanchor='center'
            ),
            font=dict(size=12)
        )
        
        # Enable pan and zoom
        fig.update_xaxes(rangeslider_visible=False)
        
        return fig, df
        
    except Exception as e:
        print(f"Error reading file {os.path.basename(file_path)}: {e}")
        return None, None


def plot_folder(folder_path, x_axis='Time (sec)'):
    """
    Plot all MarkerLocationsGA_CouchShift files in a folder and its subfolders (recursive).
    
    Parameters:
    -----------
    folder_path : str
        Relative path to folder (e.g., "2025-10-28" or "2025-10-28/Case4 - iso2 repeat")
        or absolute path
    x_axis : str
        'Time (sec)' or 'Frame No'
    """
    # Handle both relative and absolute paths
    if not os.path.isabs(folder_path):
        full_path = os.path.join(base_dir, folder_path)
    else:
        full_path = folder_path
    
    if not os.path.exists(full_path):
        print(f"Error: Folder not found: {full_path}")
        return
    
    # Recursively find all GA files in folder and subfolders
    ga_files = []
    for root, dirs, files in os.walk(full_path):
        for filename in files:
            if 'MarkerLocationsGA_CouchShift' in filename and filename.endswith('.txt'):
                file_full_path = os.path.join(root, filename)
                # Get the relative path from the selected folder
                rel_path = os.path.relpath(root, full_path)
                ga_files.append((rel_path, filename, file_full_path))
    
    ga_files.sort()  # Sort by relative path and filename
    
    if not ga_files:
        print(f"No MarkerLocationsGA_CouchShift files found in: {folder_path}")
        return
    
    selected_folder_name = os.path.basename(full_path)
    print(f"Processing {len(ga_files)} file(s) from: {selected_folder_name} (including subfolders)\n")
    
    for rel_path, filename, file_path in ga_files:
        # Construct title: Selected Folder - Subfolder - Filename
        if rel_path == '.':
            # File is directly in the selected folder
            title = f"{selected_folder_name} - {filename}"
            print(f"Loading: {filename}")
        else:
            # File is in a subfolder
            title = f"{selected_folder_name} - {rel_path} - {filename}"
            print(f"Loading: {rel_path}\\{filename}")
        
        fig, df = plot_file(file_path, x_axis, title=title)
        
        if fig is not None:
            fig.show()
            print(f"\nData Summary for {filename}:")
            print(f"Total Frames: {len(df)}")
            print(f"\nProcessing Time (ms):")
            print(f"  Mean: {df['Processing Time (ms)'].mean():.2f}")
            print(f"  Median: {df['Processing Time (ms)'].median():.2f}")
            print(f"  Min: {df['Processing Time (ms)'].min():.2f}")
            print(f"  Max: {df['Processing Time (ms)'].max():.2f}")
            print(f"\nRead Time (ms):")
            print(f"  Mean: {df['Read Time (ms)'].mean():.2f}")
            print(f"  Median: {df['Read Time (ms)'].median():.2f}")
            print(f"  Min: {df['Read Time (ms)'].min():.2f}")
            print(f"  Max: {df['Read Time (ms)'].max():.2f}")
            print("\n" + "="*80 + "\n")


def list_folders():
    """List all available folders containing MarkerLocationsGA_CouchShift files"""
    print(f"Available folders ({len(available_folders)}):\n")
    for i, (name, path) in enumerate(available_folders, 1):
        print(f"{i:2d}. {name}")
    print("\nUsage: plot_folder('folder_path', x_axis='Time (sec)')")

Usage¶

Available Functions¶

  1. list_folders() - List all available folders with data files
  2. plot_folder(folder_path, x_axis='Time (sec)') - Plot all files in a folder and its subfolders (recursive)
    • folder_path: Relative path from base directory (e.g., "2025-10-28" or "2025-10-28/Case4 - iso2 repeat")
    • x_axis: Either 'Time (sec)' or 'Frame No'
    • Searches recursively through all subfolders
  3. plot_file(file_path, x_axis_col='Time (sec)', title=None) - Plot a single file
    • title: Optional custom plot title

Step 1: List Available Folders¶

In [23]:
list_folders()
Available folders (17):

 1. 2025-03-05 CTRO Static Tests Repeat
 2. 2025-03-09 CTRO Static Tests with ImageX Phantom\Expt_9_03_2035\Couch_shift_5_5_6
 3. 2025-03-09 CTRO Static Tests with ImageX Phantom\Expt_9_03_2035\Iso_volumeview_motionview
 4. 2025-03-09 CTRO Static Tests with ImageX Phantom\Expt_9_03_2035\Marker_migrstion
 5. 2025-03-18 CTRO Robot Repeat - Static Erratic and Persistent\Erratic
 6. 2025-10-28\Case1 - iso1
 7. 2025-10-28\Case2 - shift left 5 mm
 8. 2025-10-28\Case3 - 5mm offset start, interruprt and shift according to KIM
 9. 2025-10-28\Case4 - iso2 repeat
10. 2025-11-17
11. 2025-11-17\Lat shift present
12. 2025-11-20
13. 2025-11-26
14. 2025-12-02
15. 2025-12-02\Nice
16. Static Test
17. Trt_int_prersistant_excursion

Usage: plot_folder('folder_path', x_axis='Time (sec)')

Step 2: Plot Data from a Folder¶

Uncomment and modify the example below to plot your data:

In [30]:
# Example: Plot all files in folder and subfolders with Time on x-axis (default)
plot_folder("2025-10-28")

# Example: Plot with Frame No on x-axis
# plot_folder("2025-10-28", x_axis='Frame No')

# Example: Plot specific subfolder
# plot_folder("2025-10-28\\Case4 - iso2 repeat")
Processing 5 file(s) from: 2025-10-28 (including subfolders)

Loading: Case1 - iso1\MarkerLocationsGA_CouchShift_0.txt
Data Summary for MarkerLocationsGA_CouchShift_0.txt:
Total Frames: 1006

Processing Time (ms):
  Mean: 190.57
  Median: 181.00
  Min: 0.00
  Max: 6081.00

Read Time (ms):
  Mean: 88.57
  Median: 63.00
  Min: 42.00
  Max: 492.00

================================================================================

Loading: Case2 - shift left 5 mm\MarkerLocationsGA_CouchShift_0.txt
Data Summary for MarkerLocationsGA_CouchShift_0.txt:
Total Frames: 544

Processing Time (ms):
  Mean: 217.44
  Median: 200.50
  Min: 0.00
  Max: 6625.00

Read Time (ms):
  Mean: 98.35
  Median: 67.00
  Min: 43.00
  Max: 530.00

================================================================================

Loading: Case3 - 5mm offset start, interruprt and shift according to KIM\MarkerLocationsGA_CouchShift_0.txt
Data Summary for MarkerLocationsGA_CouchShift_0.txt:
Total Frames: 237

Processing Time (ms):
  Mean: 199.82
  Median: 191.00
  Min: 0.00
  Max: 6221.00

Read Time (ms):
  Mean: 92.47
  Median: 69.00
  Min: 42.00
  Max: 524.00

================================================================================

Loading: Case3 - 5mm offset start, interruprt and shift according to KIM\MarkerLocationsGA_CouchShift_1.txt
Data Summary for MarkerLocationsGA_CouchShift_1.txt:
Total Frames: 332

Processing Time (ms):
  Mean: 197.19
  Median: 184.00
  Min: 124.00
  Max: 616.00

Read Time (ms):
  Mean: 79.17
  Median: 58.00
  Min: 45.00
  Max: 404.00

================================================================================

Loading: Case4 - iso2 repeat\MarkerLocationsGA_CouchShift_0.txt
Data Summary for MarkerLocationsGA_CouchShift_0.txt:
Total Frames: 561

Processing Time (ms):
  Mean: 205.09
  Median: 193.00
  Min: 0.00
  Max: 6462.00

Read Time (ms):
  Mean: 92.74
  Median: 63.00
  Min: 42.00
  Max: 878.00

================================================================================